package org.changchen.fgt.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.changchen.fgt.dto.LiveChannelDto;
import org.changchen.fgt.dto.LiveChannelProcess;
import org.changchen.fgt.dto.LiveChannelSourceDto;
import org.changchen.fgt.dto.LiveSourceErrorUdpateDto;
import org.changchen.fgt.service.LiveChannelService;
import org.changchen.fgt.utils.CSVUtil;
import org.changchen.fgt.utils.UrlUtil;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.text.csv.CsvRow;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class LiveChannelServiceImpl implements LiveChannelService{
	
	private static final String CSV_DIR = "/live";
	
	private static final String DEFAULT_CSV = "/channel.csv";
	
	
	private static Map<String, List<LiveChannelSourceDto>> CHANNEL_MAP = new LinkedHashMap<>();
	
	
	/**直播源处理过程数据*/
	private static Integer CURRENT_TOTAL = 0;
	private static volatile Integer CURRENT_HAVE = 0;
	private static volatile Integer CURRENT_PASS = null;
	private static Long KEY = null;
	private static Map<Long, LiveChannelProcess> PROCESS_MAP = new HashMap<>();
	
	
	@Override
	public List<LiveChannelDto> channelList() {
		
		if(CollectionUtil.isNotEmpty(CHANNEL_MAP.keySet())) {
			return this.covert2List(CHANNEL_MAP);
		}
		
		
		String channelFilePath = this.getParentDir() + CSV_DIR + DEFAULT_CSV;
		File csvFile = new File(channelFilePath);
		// 文件不存在
		if(!csvFile.exists()) {
			return Collections.emptyList();
		}
		
		List<LiveChannelSourceDto> result = new ArrayList<>();
		try {
			List<CsvRow> rows = CSVUtil.read(new FileInputStream(csvFile));
			rows.parallelStream().forEach(row -> {
				String channel = row.get(0);
				String url = row.get(1);
				LiveChannelSourceDto dto = new LiveChannelSourceDto();
				dto.setChannel(channel);
				dto.setUrl(url);
				result.add(dto);
			});
		} catch (FileNotFoundException e) {
			log.error("{}", e);
		}
		
		if(CollectionUtil.isEmpty(result)) {
			return this.covert2List(CHANNEL_MAP);
		}
		
		CHANNEL_MAP = result.stream()
				.collect(Collectors.groupingBy(LiveChannelSourceDto::getChannel, LinkedHashMap::new, Collectors.toList()));
		
		return this.covert2List(CHANNEL_MAP);
	}
	
	
	@Override
	public Boolean sourceErrorUpdate(LiveSourceErrorUdpateDto updateDto) {
		
		List<LiveChannelSourceDto> sourceList = CHANNEL_MAP.get(updateDto.getChannel());
		
		sourceList.forEach(source -> {
			if(updateDto.getUrl().equals(source.getUrl())) {
				source.setError(source.getError() + 1);
			}
		});
		
		CHANNEL_MAP.replace(updateDto.getChannel(), sourceList);
		
		return true;
	}
	
	
	
	/**   
	 * @Title:      covert2List   
	 * @Description:   
	 * @param:      @param map
	 * @param:      @return      
	 * @return:     List<LiveChannelDto>      
	 * @throws   
	 */
	private List<LiveChannelDto> covert2List(Map<String, List<LiveChannelSourceDto>> map){
		
		List<LiveChannelDto> resultList = new ArrayList<>();
		
		for(String channel : map.keySet()) {
			
			// 按请求错误数排序
			List<LiveChannelSourceDto> sourceList = map.get(channel);
			sourceList.sort((source1, source2) -> {
				return Integer.valueOf(source1.getError()).compareTo(Integer.valueOf(source2.getError()));
			});
			
			LiveChannelDto dto = new LiveChannelDto(channel, map.get(channel));
			resultList.add(dto);
		}
		
		/**
		resultList.sort((channel1, channel2) -> {
			return channel1.getChannel().compareTo(channel2.getChannel());
		});
		*/
		// 按名称排序
		resultList.sort(Comparator.comparing(LiveChannelDto::getChannel));
		
		return resultList;
	}
	
	
	/**   
	 * @Title:      addLiveChannel   
	 * @Description:新增直播源   
	 * @param:      @param file
	 * @param:      @return      
	 * @return:     Boolean      
	 * @throws   
	 */
	@Async
	@Override
	public void addLiveChannel(MultipartFile file) {
		
		try {
			
			InputStream inputStream = file.getInputStream();
			List<CsvRow> csvRows = CSVUtil.read(inputStream);
			
			// 处理进度
			CURRENT_TOTAL = csvRows.size();
			CURRENT_HAVE = 0;
			CURRENT_PASS = null;
			KEY = null;
			
			List<LiveChannelSourceDto> passList = new ArrayList<>();			
			csvRows.parallelStream().forEach(row -> {
				String channel = row.get(0);
				String url = row.get(1);
				if(UrlUtil.testLiveUrl(url)) {
					LiveChannelSourceDto dto = new LiveChannelSourceDto();
					dto.setChannel(channel);
					dto.setUrl(url);
					passList.add(dto);
				}
				CURRENT_HAVE += 1;
			});
			
			// 处理进度
			CURRENT_PASS = passList.size();
			Long key = System.currentTimeMillis();
			LiveChannelProcess process = this.getHandleProcess(null);
			KEY = key;
			process.setKey(key);
			PROCESS_MAP.clear();
			PROCESS_MAP.put(key, process);
			
			Map<String, List<LiveChannelSourceDto>> subMap = passList.stream()
					.collect(Collectors.groupingBy(LiveChannelSourceDto::getChannel, LinkedHashMap::new, Collectors.toList()));
			
			CHANNEL_MAP.putAll(subMap);
			
			// passList 写入文件保存
			File parentFile = new File(this.getParentDir() + CSV_DIR);
			if(!parentFile.exists()) {
				parentFile.mkdirs();
			}
			String channelFilePath = this.getParentDir() + CSV_DIR + DEFAULT_CSV;

			List<String[]> csvData = new ArrayList<>();
			passList.forEach(source -> {
				String[] row = new String[2];
				row[0] = source.getChannel();
				row[1] = source.getUrl();
				csvData.add(row);
			});
			CSVUtil.write(channelFilePath, csvData);

		} catch (IOException e) {
			log.error("{}", e);
		}

	}
	
	
	/**   
	 * @Title:      getHandleProcess   
	 * @Description:查看当前直播源文件处理过程   
	 * @param:      @return      
	 * @return:     LiveChannelProcess      
	 * @throws   
	 */
	@Override
	public LiveChannelProcess getHandleProcess(Long key) {
		
		if(key != null) {
			return PROCESS_MAP.get(key);
		}
		
		return LiveChannelProcess.builder()
				.total(CURRENT_TOTAL)
				.have(CURRENT_HAVE)
				.pass(CURRENT_PASS)
				.key(KEY).build();
	}
	
	
	/**   
	 * @Title:      getParentDir   
	 * @Description:获取 Jar 所在目录   
	 * @param:      @return      
	 * @return:     String      
	 * @throws   
	 */
	public String getParentDir() {
		// 获取 jar 所在目录
		ApplicationHome home = new ApplicationHome(this.getClass());
		File jarFile = home.getSource();
		return jarFile.getParentFile().toString();
	}
	

}
